{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 第5章：设计我们的交易生态系统\n",
    "\n",
    "上一章我们直接进入了 C++的实战、低延迟开发，其中我们构建了一些将在本书剩余部分使用的基石。现在我们准备开始设计我们的电子交易生态系统，这将是本书剩余部分的主要项目，我们将学习低延迟应用开发的实际原则。首先，我们将讨论我们将为端到端电子交易生态系统构建的不同低延迟组件或应用程序的高层次设计和架构。我们还将设计它们之间的抽象、组件和交互，这些将在本书的其余部分实现。\n",
    "\n",
    "在本章中，我们将涵盖以下主题：\n",
    "- 理解电子交易生态系统的布局\n",
    "- 在交易交易所设计 C++撮合引擎\n",
    "- 理解交易所如何向参与者发布信息\n",
    "- 构建市场参与者与交易所的接口\n",
    "- 设计低延迟 C++交易算法的框架\n",
    "\n",
    "让我们通过描述我们将在这本书的剩余部分设计和构建的电子交易生态系统的整体拓扑结构来开启这一章。我们将在下一节简要介绍不同的组件，然后在本章的其余部分进行更详细的讨论。有一点需要记住的是，我们将在本书中构建的电子交易生态系统是实践中存在的简化版本。这不仅是一个简化版本，而且它还是所有实际构建和运行完整电子交易生态系统所需组件的一个子集。我们选择在本书中构建的组件是因为它们是最对延迟敏感的组件，我们试图将我们的重点放在低延迟应用开发上。我们想提一下，在实践中，你将发现诸如在交易所和客户端端的历史数据捕获、与清算经纪人的连接、交易处理、会计和结算的后端系统、针对历史数据的回测框架（测试）以及许多其他组件。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 理解电子交易生态系统的布局\n",
    "\n",
    "首先，我们开始提供本书其余部分将要构建的电子交易生态系统的更高层次布局。在深入细节之前，我们首先声明这是一个简化的设计，它代表了电子交易市场实际发生的情况。简化是必要的，以便将范围限制在本书可以涵盖的内容内；然而，这仍然是对实践中发现内容的准确但简化的表示。需要注意的是，这里的目的是理解低延迟应用程序的设计和实现，因此我们要求您更多地关注我们应用 C++和计算机科学原理的应用，而不是交易生态系统本身的细节。\n",
    "\n",
    "现在，让我们通过定义和解释电子交易生态系统及其组成部分的整体拓扑结构来开始这次介绍。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义电子交易生态系统的拓扑结构\n",
    "\n",
    "让我们先通过以下图表提供一个系统的鸟瞰图：\n",
    "\n",
    "<img src=\"./images/5.1.jpg\" alt=\"图 5.1 – 简单电子交易生态系统的拓扑结构\" style=\"zoom:50%;\" />\n",
    "\n",
    "图 5.1 – 简单电子交易生态系统的拓扑结构\n",
    "\n",
    "- [中文版](./images/mermaid.md#5.1)\n",
    "\n",
    "主要组成部分，如前图所示，如下所示，根据其属于交易所侧还是交易客户端/市场参与者侧进行高级别划分。\n",
    "\n",
    "这些是交易所组件：\n",
    "- 电子交易交易所的匹配引擎(matching engine)\n",
    "- 交易交易所的订单网关(order gateway)服务器和协议编码器/解码器(encoder and decoder)\n",
    "- 交易所的市场数据编码器(encoder)和发布者(publisher)\n",
    "\n",
    "这些是交易客户端组件：\n",
    "- 对该市场数据感兴趣的市场参与者使用的市场数据消费者(consumer)和解码器(decoder)\n",
    "- 市场参与者系统中的订单网关编码器和解码器(encoder and decoder)客户端\n",
    "- 参与者系统内的交易引擎(trade engine)\n",
    "\n",
    "我们将在下一节中快速介绍这些组件中的每一个，然后在本书的其余部分详细讨论它们。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 介绍电子交易生态系统的组成部分\n",
    "\n",
    "在这里，我们将快速介绍构成电子交易生态系统的不同组件。需要注意的是，在竞争性生态系统中，每个组件都需要设计成能够以尽可能低的延迟处理事件和数据。另外，请注意，在市场波动加剧的时期，这些系统必须能够跟上并反应市场活动的大幅波动。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 介绍市场数据发布者 / market data publisher\n",
    "\n",
    "> 市场数据发布者 : 行情数据发布者\n",
    "\n",
    "交易交易所的**市场数据发布者**负责将匹配引擎维护的限价订单簿的每一项变更传达给市场参与者。与**订单网关**相比，这里的区别在于市场数据发布者发布的是面向所有参与者的公共数据，并且通常隐藏哪些订单属于哪个参与者的细节，以保持公平性。另一个区别是，订单网关基础设施只向受变更影响的订单所属的市场参与者传达订单更新，而不是所有市场参与者。市场数据发布者可以使用 TCP 或 UDP 来发布市场数据，但鉴于市场数据更新的量大，UDP 组播是首选的网络层协议。市场数据发布者还负责在发布更新之前将内部匹配引擎格式转换为市场数据格式。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 介绍匹配引擎 / matching engine\n",
    "\n",
    "> 匹配引擎 : 撮合引擎\n",
    "\n",
    "电子交易交易所的**匹配引擎**是交易交易所最关键的部分。它负责处理市场参与者对其订单的请求，并更新其维护的限价订单簿。这些请求是在客户想要添加新订单、替换现有订单、取消现有订单等情况下生成的。限价订单簿是由所有参与者发送的所有订单汇总到一个中央单一簿中，包括出价（买入订单）和要价（卖出订单）。匹配引擎还负责执行跨价格匹配的订单（即，当买入价格高于或等于卖出价格时，将买入订单与卖出订单匹配）。在特殊市场状态，如**PreOpen**（市场开盘前），**Auction**/**Opening**（市场开盘的瞬间），**PreOpenNoCancel**（可以输入订单但不能取消），等等，规则略有不同，但我们不会担心这些规则或实现它们，以保持对低延迟应用开发的关注。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 介绍交易所的订单网关服务器 / order gateway \n",
    "\n",
    "交易所的**订单网关服务器**负责接受市场参与者的连接，以便他们可以发送订单请求并在其相应订单有更新时接收通知。订单网关服务器还负责在匹配引擎格式和订单网关消息协议之间翻译消息。用于订单网关服务器的网络协议始终是 TCP，以确保消息的有序交付和可靠性。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 在市场参与者层面引入市场数据消费者 / market data consumer\n",
    "\n",
    "> 市场数据消费者 : 行情数据消费者\n",
    "\n",
    "**市场数据消费者**是市场参与者侧交易所市场数据发布组件的补充。该组件负责订阅由市场数据发布者设置的上 UDP 流或 TCP 服务器，消费市场数据更新，并将市场数据协议解码成交易引擎其他部分使用的内部格式。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 介绍订单网关编解码器客户端 / order gateway encoder and decoder client\n",
    "\n",
    "**订单网关客户端组件**是市场参与者侧交易所订单网关服务器的补充。该组件的职责是建立并维护与交易所订单网关基础设施的 TCP 连接。它还负责将策略订单请求编码为正确的交易所订单消息协议，并将交易所响应解码成交易引擎使用的内部格式。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 在市场参与者系统中介绍交易引擎 / trade engine\n",
    "\n",
    "**交易引擎**是市场参与者交易系统的核心。这是智能所在之处，也是交易决策做出的地方。该组件负责从市场数据消费者组件中消费标准化市场数据更新。它通常还会构建完整的限价订单簿，以反映市场状态，或者至少是订单簿的简化版本，这取决于交易策略的要求。它通常还会在订单簿的流动性和价格基础上构建分析，并做出自动化的交易决策。该组件使用订单网关客户端组件与交易交易所进行通信。\n",
    "\n",
    "现在我们已经介绍了电子交易生态系统中涉及的主要组件，我们将更详细地研究这些组件。首先，我们将从位于电子交易交易所系统中的匹配引擎开始。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 在交易交易所设计 C++ 匹配引擎 / match engine\n",
    "\n",
    "在本节中，我们将讨论上一节中介绍的电子交易交易所系统内的匹配引擎组件。我们首先要做的是理解匹配引擎的作用以及为什么需要它。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 理解匹配引擎的目的 \n",
    "\n",
    "在由单一交易交易所组成的电子交易生态系统中，通常情况下，有一个交易所负责接受和管理来自众多市场参与者的订单。在这种情况下，匹配引擎接受参与者可以为任何给定交易工具发送的不同类型的订单。**订单**是任何市场参与者向交易交易所发送的请求，以传达他们对购买或出售可交易产品的兴趣。每当匹配引擎从订单网关服务器基础设施接收到新订单时，它会检查这个新订单是否与现有订单的相反方交叉，以确定是否发生交易。对于本书的目的，我们假设市场参与者只发送**限价订单**并指定订单方向、数量和价格。限价订单是只能以市场参与者指定的价格或更好的价格执行的订单。\n",
    "\n",
    "到现在为止，应该很明显，匹配引擎执行的是最关键的任务，即在不同市场参与者之间执行订单匹配，并且正确、公平地执行。这里的公平是指首先处理到达交易所的订单，这种**先进先出**（FIFO）的排序在订单网关基础设施中处理，我们将在稍后讨论。那些未能立即匹配的订单留在簿中，被称为**被动订单(passive orders)**。当新的订单以跨过被动订单的价格进入时，这些订单才有资格进行匹配。这种跨过被动订单价格的订单被称为**积极订单(aggressive orders)**。\n",
    "\n",
    "匹配引擎将所有市场参与者发送的所有被动订单排列到一个称为**订单簿**的数据结构中。这个订单簿的细节将是我们下次讨论的主题。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 理解交易所订单簿 / exchange order book\n",
    "\n",
    "限价订单簿(limit order book) 包含了所有市场参与者针对单一交易工具的所有被动限价订单。这些订单通常按照从最高买入价到最低买入价排列被动买入订单，以及从最低卖出价到最高卖出价排列被动卖出订单。这种排序方式直观且自然，因为被动买入订单是从最高买入价到最低买入价进行匹配，而被动卖出订单是从最低卖出价到最高卖出价进行匹配。对于同一方和相同价格的订单，它们将根据发送时间按照先进先出（FIFO）的顺序排列。请注意，FIFO 只是排序标准之一；现代电子交易市场有不同的匹配算法类型，例如按比例分配（Pro Rata）以及一些 FIFO 和按比例分配的混合。按比例分配是一种匹配算法，其中较大的订单无论在 FIFO 队列中的位置如何，都会从积极订单中获得更大的成交量。对于我们的匹配引擎，我们只将实现 FIFO 匹配算法。\n",
    "\n",
    "> “价格优先”是撮合规则中的核心原则，意味着在限价订单簿中，价格更优的被动订单（买单价格更高、卖单价格更低）会先于价格较差的订单获得与主动订单成交的机会。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要完全理解订单簿的工作原理，我们将查看市场发生的一些场景以及它们如何影响订单簿。让我们首先确定订单簿的初始状态。假设有三个不同的市场参与者——客户 A、B 和 C 在买卖双方有订单。\n",
    "\n",
    "| 买入                             | 卖出                            |\n",
    "| -------------------------------- | ------------------------------- |\n",
    "| 客户 A 订单号 1 买入 20 @ 10.90  | 客户 B 订单号 5 卖出 10 @ 11.00 |\n",
    "| 客户 A 订单号 2 买入 10 @ 10.80  | 客户 C 订单号 6 卖出 5 @ 11.00  |\n",
    "| 客户 B 订单号 3 买入 5 @ 10.80   | 客户 B 订单号 7 卖出 5 @ 11.10  |\n",
    "| 客户 C 订单号 4 买入 100 @ 10.70 |                                 |\n",
    "\n",
    "表 5.1 – 包含一些订单的初始限价订单簿状态"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这里，客户 A 有 2 个被动买入订单，数量分别为 20 和 10，价格分别为 10.90 和 10.80。客户 B 有一个数量为 5 的买入订单，价格为 10.80，以及 2 个卖出订单，数量分别为 10 和 5，价格分别为 11.00 和 11.10。客户 C 有 2 个被动订单，分别是数量为 5 的买入订单和数量为 5 的卖出订单，价格分别为 10.80 和 11.00。现在，假设客户 A 发送一个新的买入订单，数量为 10，价格为 10.90，而客户 B 发送一个新的卖出订单，数量为 10，价格为 11.20。更新的订单簿如下表所示，新的订单被突出显示。由于 FIFO 排序，新的订单号*OrderId=8*在相同价格下位于订单号*OrderId=1*的买入订单之后。\n",
    "\n",
    "| 买入                             | 卖出                            |\n",
    "| -------------------------------- | ------------------------------- |\n",
    "| 客户 A 订单号 1 买入 20 @ 10.90         | 客户 B 订单号 5 卖出 10 @ 11.00         |\n",
    "| **客户 A 订单号 8 买入 10 @** **10.90** | 客户 C 订单号 6 卖出 5 @ 11.00          |\n",
    "| 客户 A 订单号 2 买入 10 @ 10.80         | 客户 B 订单号 7 卖出 5 @ 11.10          |\n",
    "| 客户 B 订单号 3 买入 5 @ 10.80          | **客户 B 订单号 9 卖出 10 @** **11.20** |\n",
    "| 客户 C 订单号 4 买入 100 @ 10.70        |                                         |\n",
    "\n",
    "表 5.2 – 新增订单后的更新订单簿"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，假设客户 A 将订单号*OrderId=2*的数量从 10 增加到 20。当以这种方式增加订单的数量时，该订单在 FIFO 排序中会失去优先级，并移至该价格级别的队列末尾。我们还假设客户 B 将订单号*OrderId=5*的数量从 10 减少到 1。请注意，根据市场规则，当减少订单的数量时，它不会失去队列中的优先级，并且仍然保持在原位。更新的订单簿如下所示，受影响的订单被突出显示：\n",
    "\n",
    "| 买入                             | 卖出                            |\n",
    "| -------------------------------- | ------------------------------- |\n",
    "| 客户 A 订单号 1 买入 20 @ 10.90         | **客户 B 订单号 5 卖出 1 @** **11.00** |\n",
    "| 客户 A 订单号 8 买入 10 @ 10.90         | 客户 C 订单号 6 卖出 5 @ 11.00         |\n",
    "| 客户 A 订单号 3 买入 5 @ 10.80          | 客户 B 订单号 7 卖出 5 @ 11.10         |\n",
    "| **客户 B 订单号 2 买入 20 @** **10.80** | 客户 B 订单号 9 卖出 10 @ 11.20        |\n",
    "| 客户 C 订单号 4 买入 100 @ 10.70        |                                        |\n",
    "\n",
    "表 5.3 – 修改订单后的订单簿状态\n",
    "\n",
    "> 在同一价格档位内，增加现有订单数量会重排其 FIFO 优先级并将其移至该档位队;而减少数量则不影响原有队列顺序，订单保持原位。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后，让我们假设客户端 A 将订单 ID 为 4 的买入订单从 10.70 的价格修改为 10.90 的价格，数量没有变化。此订单操作的影响相当于取消订单并以新价格发送新订单。让我们还假设客户端 B 决定他们不再需要订单 ID 为 9 的卖出订单，并发送了取消请求。由于这两个操作，下一个显示的更新订单簿中，修改后的订单被突出显示，取消的订单已从订单簿中移除：\n",
    "\n",
    "| 买入                                        | 卖出                                      |\n",
    "| ------------------------------------------- | ----------------------------------------- |\n",
    "| 客户端 A 订单 ID 1 买入 20 @ 10.90          | **客户端 B 订单 ID 5 卖出 1 @** **11.00** |\n",
    "| 客户端 A 订单 ID 8 买入 10 @ 10.90          | 客户端 C 订单 ID 6 卖出 5 @ 11.00         |\n",
    "| **客户端 C 订单 ID 4 买入 100 @** **10.90** | 客户端 B 订单 ID 7 卖出 5 @ 11.10         |\n",
    "| 客户端 A 订单 ID 3 买入 5 @ 10.80           |                                           |\n",
    "| 客户端 B 订单 ID 2 买入 20 @ 10.80          |                                           |\n",
    "\n",
    "表 5.4 – 修改和取消操作后的限价订单簿状态\n",
    "\n",
    "> 将现有订单改价等同于先取消原订单再以新价格重新下单，因而失去原有优先级；而直接取消订单会将其从订单簿中彻底移除。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "到目前为止，在我们讨论的场景中，还没有发生交易，因为订单活动是这样的，所有买入订单的价格都低于所有卖出订单的价格。让我们在下一节中继续讨论，看看当有一个可以跨越买入或卖出订单价格的积极订单时会发生什么，以及它会产生什么影响。\n",
    "\n",
    "关于订单修改，有两点需要注意如下：\n",
    "\n",
    "- 当订单修改以减少数量时，订单在队列中的优先级或位置不会改变\n",
    "- 当订单修改以增加数量或修改订单价格时，它具有取消订单并发送带有新价格和数量值的订单的等效效果（即，将分配一个新的优先级）\n",
    "\n",
    "在下一节中，我们将探讨匹配引擎需要执行的下一个大任务——匹配相互交叉的参与者订单。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 匹配参与者订单 / matching participant orders\n",
    "\n",
    "在本节中，我们将了解当市场参与者修改现有订单或以某种方式发送新订单，使得该订单的价格将导致与另一侧的现有被动订单匹配时会发生什么。在这种情况下，匹配引擎将此积极订单与从最积极到最不积极的价位的被动订单进行匹配。这意味着被动买单从最高到最低的买入价格进行匹配，被动卖单从最低到最高的卖出价格进行匹配。在被动订单未完全匹配，因为积极订单的数量小于另一侧的被动流动性时，剩余的流动性将保留在订单簿中。在积极订单未完全匹配，因为另一侧的被动流动性少于积极订单的数量时，剩余的数量将作为被动订单保留在订单簿中。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "让我们了解匹配参与订单的不同情况，并假设订单簿的状态是我们在上一个部分留下的，如下所示：\n",
    "\n",
    "| 买入                             | 卖出                            |\n",
    "| -------------------------------- | ------------------------------- |\n",
    "| 客户 A 订单号 1 买入 20 @ 10.90  | 客户 B 订单号 5 卖出 1 @ 11.00 |\n",
    "| 客户 A 订单号 8 买入 10 @ 10.90  | 客户 C 订单号 6 卖出 5 @ 11.00 |\n",
    "| 客户 C 订单号 4 买入 100 @ 10.90 | 客户 B 订单号 7 卖出 5 @ 11.10 |\n",
    "| 客户 A 订单号 3 买入 5 @ 10.80   |                                |\n",
    "| 客户 B 订单号 2 买入 20 @ 10.80  |                                |\n",
    "\n",
    "表 5.5 – 任何订单匹配之前的订单簿初始状态"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，让我们假设客户 C 发送了一个数量为 50，卖出价格为 10.90 的卖出订单。这将导致卖出订单与 *订单号=1* 和 *订单号=8* 的买入订单完全匹配，并且与 *订单号=4* 的买入订单部分匹配，数量为 20，剩余数量为 80。完全匹配的订单将从订单簿中移除，部分匹配的订单将修改为新剩余数量。这次匹配交易后的更新订单簿如下所示：\n",
    "\n",
    "| 买入                             | 卖出                            |\n",
    "| -------------------------------- | ------------------------------- |\n",
    "| **客户 C 订单号 4 买入** **80 @** **10.90** | 客户 B 订单号 5 卖出 1 @ 11.00 |\n",
    "| 客户 A 订单号 3 买入 5 @ 10.80              | 客户 C 订单号 6 卖出 5 @ 11.00 |\n",
    "| 客户 B 订单号 2 买入 20 @ 10.80             | 客户 B 订单号 7 卖出 5 @ 11.10 |\n",
    "\n",
    "表 5.6 – 反映激进订单和部分执行影响的订单簿\n",
    "\n",
    "> 当卖出限价单以价格优先进入簿时，会依序与当前最佳买单成交；被完全匹配的买单从订单簿移除，若某买单仅部分成交，则其数量减至剩余值并继续按原 FIFO 优先级留在同一价格档位。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，让我们假设客户 A 发送了一个数量为 10，买入价格为 11.00 的买入订单。这将完全匹配 *订单号=5* 和 *订单号=6* 的卖出订单，并且激进买入订单上的剩余未匹配数量作为被动买入订单留在簿中。这次匹配交易后的更新订单簿如下所示：\n",
    "\n",
    "| 买入                             | 卖出                            |\n",
    "| -------------------------------- | ------------------------------- |\n",
    "| **客户 A 订单号 9 买入** **4 @** **11.00** | 客户 B 订单号 7 卖出 5 @ 11.10 |\n",
    "| 客户 C 订单号 4 买入 80 @ 10.90            |                                |\n",
    "| 客户 A 订单号 3 买入 5 @ 10.80             |                                |\n",
    "| 客户 B 订单号 2 买入 20 @ 10.80            |                                |\n",
    "\n",
    "表 5.7 – 完全执行后的订单簿和激进方剩余数量\n",
    "\n",
    "> 当市场参与者提交一张可成交的买入限价单时，会按价格优先、时间优先依次撮合当前最佳卖单直至数量耗尽；若仍有剩余数量，则以该订单的限价作为被动买单留在簿中，并按新订单的时间进入对应价位的 FIFO 队列。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在我们已经了解了在匹配引擎中会遇到的大量常见交互以及它们是如何处理的，以及它们与限价订单簿的交互方式，我们可以设计本书中将要构建的匹配引擎。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 设计我们的匹配引擎\n",
    "\n",
    "我们将在本书的剩余部分实现本章中讨论的每个 C++ 电子商务生态系统组件。然而，在我们开始下一章之前，了解这些组件的架构对于使实现细节更容易和更清晰是很重要的。我们在这里只展示了 *图 5.1* 中的匹配引擎组件，这样我们可以更详细地讨论我们的匹配引擎设计：\n",
    "\n",
    "<img src=\"./images/5.2.jpg\" alt=\"图 5.2 – 匹配引擎组件设计\" style=\"zoom:100%;\" />\n",
    "\n",
    "- [中文版](./images/mermaid.md#5.2)\n",
    "\n",
    "图 5.2 – 匹配引擎组件设计\n",
    "\n",
    "与*图 5.1*相比，我们在本图中提供了更多细节，接下来我们将讨论匹配引擎的主要设计选择。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 线程模型\n",
    "\n",
    "在我们的系统中，匹配引擎、市场数据发布者和订单网关服务器将是**独立的线程**。这是故意的，以便每个组件都可以独立运行，在市场活动高峰期间，整个系统可以达到最大吞吐量。此外，每个组件还需要执行其他任务——例如，订单网关服务器必须与所有市场参与者保持连接，即使匹配引擎正忙也是如此。同样，让我们假设市场数据发布者正忙于在网络中发送市场数据；我们不希望匹配引擎或订单网关服务器减慢速度。我们已经在上一章的“为低延迟应用构建 C++构建块”部分中的“C++多线程低延迟应用”部分中看到了如何创建线程、设置它们的亲和性以及为它们分配任务。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 线程间的通信\n",
    "\n",
    "在这里需要讨论的另一件重要事情是匹配引擎与订单网关服务器基础设施之间的通信。订单网关服务器将来自市场参与者的订单请求序列化，并将它们转发给匹配引擎进行处理。匹配引擎需要为订单请求生成响应并将它们发送回订单网关服务器。此外，它还需要通知订单网关服务器参与者订单上的执行情况，以便他们可以了解交易情况。因此，需要一个双向队列，或者从订单网关服务器到匹配引擎的一个队列，以及从匹配引擎到订单网关服务器的一个队列。\n",
    "\n",
    "另一个通信渠道是当匹配引擎生成并发送市场数据更新，以反映公共市场数据发布组件的限价订单簿更新状态时。\n",
    "\n",
    "最后，由于匹配引擎、订单网关服务器和市场数据发布者都是不同的线程，这里我们找到了一个无锁队列的完美案例。我们将使用我们在上一章的“使用无锁队列传输数据”部分中创建的无锁 FIFO 队列。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 限价订单簿\n",
    "\n",
    "最后，对于限价订单簿，我们将使用几种不同的数据结构来高效地实现它。在不深入具体实现细节（我们将在下一章中探讨）的情况下，我们需要在双方都保持正确的排序顺序来维护买卖报价，以便在积极订单到来时进行高效的匹配。我们需要能够高效地在价格级别中插入和删除订单，以支持基于客户请求的添加、修改和删除订单等操作。在这里，另一个特别重要的考虑因素是，我们使用的数据结构和订单对象本身必须避免动态内存分配，并尽可能少地复制数据。我们将在上一章的“设计 C++内存池以避免动态内存分配”部分中大量使用我们创建的内存池。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 理解交易所如何向参与者发布信息\n",
    "\n",
    "上一节专门讨论了匹配引擎的细节，在讨论中，我们假设匹配引擎从订单网关服务器基础设施接收市场参与者的订单请求。我们还隐含地假设匹配引擎会将其维护的限价订单簿的变化通知给所有监听市场数据馈送的市场参与者。在本节中，我们将讨论匹配引擎依赖以与市场参与者通信的市场数据发布者和订单网关服务器组件。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 通过市场数据通信市场事件\n",
    "\n",
    "让我们先讨论市场数据发布者组件。这个组件负责将匹配引擎维护的限价订单簿的更新转换为。我们之前提到，市场数据网络层协议可以是 TCP 或 UDP，但通常，实践中首选的协议是 UDP，我们将在我们的市场数据发布者中也使用这个协议。\n",
    "\n",
    "简而言之，市场数据协议代表了市场数据发布者通过 UDP（或在某些情况下 TCP）协议发布消息的格式。**FIX Adapted for STreaming（FAST）**是目前许多电子交易交易所使用的最知名和最受欢迎的市场数据消息格式。还有其他协议，如**ITCH**、**PITCH**、**Enhanced Order Book Interface**（**EOBI**）、**Simple Binary Encoding**（**SBE**）等，但为了本书的目的，我们将创建一个简单的自定义二进制协议，如 EOBI 或 SBE，我们将使用它。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于 FIX 是金融应用中最常用的协议，我们将在此处介绍一些细节。FIX 数据组织成一系列`标签`=`值`样式的字段。通过一个简单的例子更容易理解这一点，所以对于一个假设的市场数据更新，你可能收到以下一系列字段来传达该更新的所有数据。这个假设的市场数据更新对应于向苹果公司股票（股票代码 AAPL，数值安全 ID 68475）添加一个数量为 1,000 的新买盘订单，价格为 175.16。\n",
    "\n",
    "| **标签** | **修正名称**   | **值**       | **描述**             |\n",
    "| -------- | -------------- | ------------ | -------------------- |\n",
    "| 268      | NoMDEntries    | 1            | 市场数据更新数量     |\n",
    "| 279      | MDUpdateAction | 0 (New)      | 市场数据更新类型     |\n",
    "| 269      | MDEntryType    | 0 (Bid)      | 市场数据条目类型     |\n",
    "| 48       | SecurityID     | 68475 (AAPL) | 交易产品的整数标识符 |\n",
    "| 270      | MDEntryPx      | 175.16       | 本市场数据更新的价格 |\n",
    "| 271      | MDEntrySize    | 1000         | 本市场数据更新的数量 |\n",
    "| ...      | ...            | ...          | ...                  |\n",
    "\n",
    "表 5.8 – 一个对应于假设市场数据更新的 FIX 消息示例\n",
    "\n",
    "图 5.3 – 交易所发送的不同市场更新\n",
    "\n",
    "<img src=\"./images/5.3.jpg\" alt=\"图 5.3 – 交易所发送的不同市场更新\" style=\"zoom:50%;\" />\n",
    "\n",
    "- [中文版](./images/mermaid.md#5.3)\n",
    "\n",
    "让我们接下来讨论这些内容。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 市场状态变化\n",
    "\n",
    "这些消息通知市场参与者关于市场变化和/或撮合引擎状态的变化。通常，市场会经历诸如**关闭**（用于交易）、**开盘前**（常规交易会话前的市场状态）、**开盘**（当市场从开盘前状态过渡到交易状态时）和**交易**（常规交易会话）等状态。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 交易品种更新 / Instrument updates\n",
    "\n",
    "> 一些术语不如AI翻译\n",
    "\n",
    "交易所通过“交易品种更新”消息，向市场参与者发布可供交易的各类交易品种信息。有些交易所支持市场参与者即时创建的特殊交易品种，此类消息也用于通告这些品种的任何变化。通常，这些消息包含交易品种的元数据信息，例如最小报价变动单位、最小变动价位的货币价值等。\n",
    "\n",
    "- **最小报价变动单位（minimum price increment）**：订单价格之间允许的最小价差。在前面的示例中，我们假定最小报价变动单位为 0.10，即有效价格必须是 0.10 的倍数。\n",
    "- **最小变动价位的货币价值（tick size value）**：当我们以相差一个最小报价变动单位的价格买入和卖出时，所能获得的盈利或亏损金额。\n",
    "\n",
    "对于股票、交易所交易基金（ETF）等产品，最小变动价位乘数通常为 1，这意味着两笔买卖的盈亏就是卖出价减去买入价。ETF 是在交易所上市的证券，是由一篮子资产构成的投资工具；通过投资 ETF，相当于同时持有构成该基金的整组资产。\n",
    "\n",
    "然而，对于期货、期权等杠杆产品，这个乘数可能不等于 1，此时最终盈亏按以下公式计算：\n",
    "\n",
    "$\\frac{\\text{卖出价} - \\text{买入价}}{\\text{最小报价变动单位}} \\times \\text{成交数量} \\times \\text{最小变动价位的货币价值}$\n",
    "\n",
    "> `((sell-price – buy-price) / min-price-increment) * trade-qty *` `tick-size`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 订单更新 / Order updates\n",
    "\n",
    "行情数据发布端会使用“订单更新”消息，通报撮合引擎维护的限价订单簿中各类订单的变动——即我们在“交易所中设计 C++ 撮合引擎”章节的“理解交易所订单簿”小节中讨论过的那类更新。通常，订单更新消息包括以下几种类型：\n",
    "\n",
    "- **订单新增（Order Add）**：用于通知市场参与者有新的被动订单被加入限价订单簿。典型字段包括交易品种 ID (instrument-id)、订单 ID (order-id)、价格 (price)、买卖方向(side)、数量 (quantity)以及优先级 (priority)。其中优先级字段用来指明该订单在该价格档位 FIFO 队列中的位置。\n",
    "- **订单修改（Order Modify）**：用于告知市场参与者某个被动订单在价格、数量或两者上发生了修改。该消息的字段与订单新增消息类似。如前所述，在大多数情况下（除非订单数量减少），订单修改事件会重新分配一个新的订单优先级值。\n",
    "- **订单删除（Order Delete）**：用于通知市场参与者某个被动订单已经从订单簿中删除。关键字段为交易品种 ID 和订单 ID，用以指明从订单簿中移除的具体订单。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 成交消息（Trade messages）\n",
    "\n",
    "交易所通过“成交消息”通知市场参与者市场中发生了撮合成交。该消息通常包含的字段有：交易品种 ID (instrument-id)、主动订单的方向（买/卖）、成交价格以及成交数量。\n",
    "\n",
    "在成交产生的同时，交易所通常会同步发布必要数量的订单删除（Order Delete）、订单修改（Order Modify）和订单新增（Order Add）消息，以说明哪些订单已被全部或部分成交，并据此从订单簿中移除或调整，以反映订单簿的最新状态。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 市场统计（Market statistics）\n",
    "\n",
    "这是部分交易所可选发布的消息，用于传递特定交易品种的各类统计数据。统计内容可能包括某品种的成交量、未平仓量（Open Interest）、最高价、最低价、开盘价、收盘价等。\n",
    "\n",
    "我们已经介绍了大量行情数据消息类型及其传递的信息，现在可以着手设计将在电子交易所中构建的行情数据发布组件。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 设计行情数据发布器（Designing the market data publisher）\n",
    "\n",
    "接下来讨论我们将在电子交易所中实现的行情数据发布器的若干设计细节。此处仅聚焦于图 5.1 中的行情发布端，以便深入分析其结构。\n",
    "\n",
    "图 5.4 – 我们的行情数据发布基础设施设计\n",
    "\n",
    "<img src=\"./images/5.4.jpg\" alt=\"图 5.4 – 我们的行情数据发布基础设施设计\" style=\"zoom:100%;\" />\n",
    "\n",
    "- [中文版](./images/mermaid.md#5.4)\n",
    "\n",
    "行情数据发布基础设施包含两个核心组件，它们都利用上一章“使用套接字进行 C++ 网络编程”部分构建的套接字工具，将行情数据发送到网络上。同时，我们还会使用自建的线程库，用于创建、启动并运行行情数据发布线程。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 行情数据协议编码器（Market data protocol encoder）\n",
    "\n",
    "行情数据发布基础设施中的“行情数据协议编码器”负责将撮合引擎发布的行情更新进行编码。该编码器消费反映订单簿变化的行情更新，并加上一些补充信息，将其转换为公开的行情消息格式。同时，它会把增量行情更新发布到为增量流所配置的 UDP 组播流中。需要强调的是，增量流只包含用于更新订单簿的市场变动，前提是订阅方在收到该增量更新前，对限价订单簿的视图是准确的。编码后的行情更新也会发送给快照合成器组件，我们会在下一节详细讨论。\n",
    "\n",
    "行情数据流在网络流量上通常非常巨大，尤其在市场高度波动的时期，会出现显著的流量峰值。由于 TCP 协议需要为消息确认和丢包重传额外消耗带宽，行情数据通常采用 UDP 作为网络传输协议。基于 UDP 的组播流也更受欢迎，因为市场只需在组播流上发布一次行情数据，所有感兴趣的订阅方便可直接订阅该流，而无需像 TCP 那样与每个消费者分别建立一对一连接。\n",
    "\n",
    "这种设计并非没有缺点：例如行情消费者可能因网络拥塞、硬件或软件性能不足等原因丢失 UDP 包。一旦发生此类情况，交易客户端维护的订单簿就会失真，因为它们可能漏掉了某些订单新增、修改或取消的更新。快照组播流(the snapshot multicast streams)正是为解决这一问题而存在——我们将在后续章节实现行情消费者时结合示例深入讲解，而下一节将先简要介绍快照合成器组件。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 快照合成器（Snapshot synthesizer）\n",
    "\n",
    "快照合成器消费由行情数据协议编码器发布的已编码行情更新，据此合成最新的限价订单簿快照，并周期性地将快照发布到快照组播流。关键在于，快照合成过程不会干扰增量流的发布，从而保证增量订单簿更新可以尽可能快速地对外推送。快照合成器以独立线程运行，其唯一职责是基于增量更新生成准确的订单簿快照。\n",
    "\n",
    "该组件还会在快照更新中附带正确的序列信息，以便客户端在订阅快照 UDP 组播流后能够完成同步。也就是说，快照消息会携带用于生成该快照的增量流最后一个序列号，便于下游行情消费者根据该序列号执行补齐与同步操作。当我们在后续章节实现行情发布器与行情消费者时，会结合示例详细说明这一机制。\n",
    "\n",
    "需要注意的是，系统中其他组件所强调的低延迟要求在此并不适用，因为快照流本身就是延迟且降采样后的信息。客户端侧丢包极为罕见，同时快照同步流程本身也较慢，因此没有必要刻意追求极致低延迟。为了简化实现，我们的快照合成器同样使用 UDP 进行传输，但在实务中往往会结合 TCP 与 UDP。对于追求极低延迟的市场参与者而言，UDP 流上的丢包被认为是罕见的，因为与交易所之间及中间交换机通常具备高带宽、低时延；同时，参与者也会投入资源部署快速服务器、开发低延迟行情消费软件，并使用专门的网络接口卡（NIC, Network Interface Cards）以处理大量行情数据。\n",
    "\n",
    "至此，我们完成了本书中将要构建的行情数据发布基础设施的高层设计。下一步，将讨论交易所用于通知市场参与者其订单请求回复及订单成交情况的另一条通道——订单网关接口。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 通过订单网关接口通知市场参与者\n",
    "\n",
    "我们已经讨论过，电子交易所利用行情数据发布端向市场参与者传播关于各类交易品种的订单簿变化以及成交情况的公开信息。这类市场数据对所有能够访问并订阅行情数据流的参与者开放。本节将探讨交易所用于与市场参与者沟通其自身订单更新的另一条接口——订单网关接口。\n",
    "\n",
    "公开行情数据源与订单网关基础设施提供的信息存在几个关键差异，我们将在后续小节逐一分析"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 认识网络协议的差异\n",
    "\n",
    "如前所述，但在此再强调一次：\n",
    "\n",
    "- **行情数据发布端通常使用 UDP 协议。** 行情数据量巨大且需要尽快播发，因此采用无需握手与确认的 UDP，以降低延迟。不过，为应对偶发的丢包，行情发布器会提供额外的同步机制。\n",
    "- **订单网关基础设施通常使用 TCP 协议。** 订单网关的职责是与客户进行可靠通信，如果出现丢包而没有 TCP 的可靠传输保障，处理起来会非常困难。试想若客户无法确认订单是否到达交易所，或无法确信订单状态更新和成交通知是否即时送达，那将造成巨大的运营压力。\n",
    "\n",
    "因此，UDP 与 TCP 的选择反映了两条通道对延迟与可靠性截然不同的要求。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 区分公共信息与私有信息\n",
    "\n",
    "市场数据发布端与订单网关基础设施最大的差异在于：\n",
    "\n",
    "- **行情数据发布端提供的是公共信息。** 为保护敏感内容，它不会暴露订单归属的客户、成交中涉及的具体客户等细节。所有市场参与者都能订阅这些公开数据，用于重建限价订单簿，掌握交易品种的整体状态。\n",
    "- **订单网关服务器发布的是私有信息。** 它只向拥有相关订单的客户推送订单状态更新。换句话说，参与者无需在订单簿中有任何订单就可以接收与处理公共行情数据；但若要收到专属于自己的订单网关通知，必须首先在订单簿中存在属于自己的订单，否则交易所无任何私密内容需要告知该客户。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 为参与者订单发送请求\n",
    "\n",
    "另一个显而易见的差异是：**订单网关组件提供的是双向通信通道**。客户端可以通过它向交易所发送各种订单请求，例如新增订单、修改订单、取消订单等。与此同时，交易所也借助订单网关向相应的市场参与者发送其订单的私有通知。\n",
    "\n",
    "相比之下，行情数据发布基础设施通常并不响应任何客户端请求——通信路径是单向的，仅从交易所流向行情订阅者。\n",
    "\n",
    "综上，电子交易所侧最后需要设计的发送组件就是我们刚刚讨论的订单网关基础设施，下一节我们将着手展开。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 设计订单网关服务器\n",
    "\n",
    "接下来我们来讨论将在电子交易所中实现的订单网关服务器的若干设计细节。此处仅展示图 5.1 中的订单网关服务器基础设施，以便更详细地讨论订单网关服务器的设计。\n",
    "\n",
    "图 5.5 – 我们的订单网关服务器基础设施设计\n",
    "\n",
    "<img src=\"./images/5.5.jpg\" alt=\"图 5.5 – 我们的订单网关服务器基础设施设计\" style=\"zoom:100%;\" />\n",
    "\n",
    "- [中文版](./images/mermaid.md#5.5)\n",
    "\n",
    "相较于图 5.1，这张图展示了更多细节，并拆分了订单网关服务器基础设施的子组件。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### TCP 连接服务器/管理器\n",
    "\n",
    "订单网关基础设施中的第一个组件是 TCP 连接管理器。该组件负责建立一个 TCP 服务器，以监听并接受来自市场参与者订单网关客户端的入站 TCP 连接；同时负责检测客户端断开连接的情况，并将其从活跃连接列表中移除。最后，该组件需要将撮合引擎返回的订单响应转发给相应的目标客户端。我们将使用上一章《为低延迟应用构建 C++ 基础模块》中“使用套接字进行 C++ 网络编程”部分实现的套接字工具、TCP 套接字以及 TCP 服务器功能。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### FIFO 排序器 / FIFO sequencer\n",
    "\n",
    "Another important task this component needs to do is maintain fairness when processing requests from market participants. As mentioned previously, to maintain fairness, client responses must be processed in the exact order they were received at the exchange infrastructure. So, the FIFO sequencer must ensure that it forwards client requests to the matching engine across the different client connections that the TCP connection manager maintains in the order in which they are received.\n",
    "\n",
    "该组件另一个重要任务是保持处理市场参与者请求时的公平性。正如前面所述，为了维持公平，客户端响应必须按照它们在交易所基础设施中接收的精确顺序进行处理。因此，FIFO 排序器必须确保在 TCP 连接管理器所维护的不同客户端连接之间，按照接收顺序将客户端请求转发给撮合引擎。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 交易所消息协议解码与编码器 / Exchange messaging protocol decoder and encoder\n",
    "\n",
    "The encoder-decoder component is responsible for translating between the exchange messaging protocol and whatever internal structures the matching engine expects client requests in and publishes client responses in. Depending on the complexity of the exchange protocol, this can be as straightforward as packaging and extracting the correct fields into and from a packed binary structure. If the exchange messaging format is more complicated, then there will be additional encoding and decoding steps involved. For this book’s purposes, we will have a simple exchange order messaging protocol that uses packed binary structures and has additional information on top of the format the exchange matching engine uses.\n",
    "\n",
    "This concludes our discussion of the electronic trading exchange, and now we can move on to building the client-side infrastructure for a market participant that wishes to trade on this exchange.\n",
    "\n",
    "编码-解码组件负责在交易所消息协议与撮合引擎所期望的内部结构之间进行转换：撮合引擎既会以该内部结构接收客户端请求，也会按照该结构发布客户端响应。根据交易所协议的复杂度，这项工作有时只是将所需字段打包或从打包的二进制结构中解出；如果交易所消息格式更复杂，就需要额外的编码与解码步骤。在本书中，我们将使用一种简单的交易所订单消息协议，它基于打包的二进制结构，并在交易所撮合引擎使用的格式之上附加了额外信息。\n",
    "\n",
    "至此，我们完成了对电子交易所的讨论，接下来可以着手构建市场参与者在该交易所上进行交易所需的客户端基础设施。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 构建市场参与者的交易所接口 / Building a market participant’s interface to the exchange\n",
    "\n",
    "We will now discuss the purpose and design of the components in the market participants’ systems. Specifically, we will start by discussing the market data consumer in the client’s trading system, which subscribes to, consumes, and decodes the public market data published by the exchange. We will also discuss the order gateway client infrastructure in the client’s trading system, which connects to the exchange order gateway server. The order gateway client is also responsible for sending order requests to the exchange and receiving and decoding responses for the client’s orders.\n",
    "\n",
    "接下来，我们讨论市场参与者系统中各组件的用途与设计。我们将首先介绍客户端交易系统中的行情数据消费者，它订阅、接收并解码交易所发布的公共行情数据。随后，还会介绍客户端交易系统中的订单网关客户端基础设施，该组件负责连接至交易所的订单网关服务器。订单网关客户端还负责向交易所发送订单请求，并接收与解码有关客户订单的响应。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 理解行情数据消费者基础设施 / Understanding the market data consumer infrastructure\n",
    "The market data consumer component in a market participant’s trading system is the direct complement of the market data publisher component in the electronic trading exchange. It is responsible for subscribing to and consuming the multicast network traffic that the exchange publishes, decoding and normalizing the market data it reads from the exchange protocol to an internal format, and implementing packet drop-related synchronization mechanisms.\n",
    "\n",
    "市场参与者交易系统中的行情数据消费者组件，与电子交易所的行情数据发布器组件相对应。它负责订阅并消费交易所发布的组播网络流量，将从交易所协议读取的行情数据解码并规范化为内部格式，并实现针对丢包的同步机制。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 订阅并消费 UDP 组播流量 / Subscribing to and consuming UDP multicast traffic\n",
    "\n",
    "The first and most obvious task is to subscribe to the multicast stream that the exchange is publishing market data on. Typically, in the interest of load balancing, the exchange groups different trading instruments on different multicast stream addresses. This lets clients choose a subset of all the data that the exchange is publishing, depending on the trading instrument and products that the client is interested in. Typically, this involves the clients joining the correct multicast streams, the addresses of which are public information available from the exchange.\n",
    "\n",
    "最首要的任务是订阅交易所发布行情数据的组播流。通常为了负载均衡，交易所会将不同的交易品种分配到不同的组播地址上。这使得客户端可以根据自身感兴趣的交易品种和产品，选择交易所发布数据的子集。一般而言，这需要客户端加入正确的组播流，而这些地址是交易所公开提供的信息。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 从交易所协议解码并规范化 / Decoding and normalizing from exchange protocol\n",
    "\n",
    "The next thing the market data consumer needs to do is convert from the exchange market data protocol into an internal format that the rest of the components in the participant’s system use. Depending on the exchange market data protocol, this part of the component can vary in complexity and performance latencies. The fastest protocols are the ones where minimal decoding is required, such as EOBI and SBE, which are just binary-packed structures. What this means is that the market data format is such that decoding the stream just involves reinterpreting the byte stream as the binary-packed structure we expect to find in the stream and is as fast as it gets. More complicated protocols such as FAST generally take longer to decode and normalize.\n",
    "\n",
    "接下来，行情数据消费者需要将交易所的行情数据协议转换为参与者系统其他组件使用的内部格式。根据交易所行情数据协议的不同，此步骤的复杂度与性能延迟也会有所差异。最快的协议是那些几乎无需解码的协议，例如 EOBI 和 SBE，这些都是打包的二进制结构。这意味着行情数据格式允许我们只需将字节流重新解释为预期的二进制结构，速度极快。而更复杂的协议（如 FAST）通常需要更长的时间来解码与规范化。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 启动与丢包时的同步 / Synchronizing on startup and packet drops\n",
    "\n",
    "Remember that we discussed that usually exchanges prefer UDP to be the network protocol to deliver market data to the participants. While this speeds up the delivery of data to clients and achieves higher throughput, this also leaves us open to packet drops and out-of-order delivery due to the unreliable nature of UDP. To make sure that market participants see market data packets in the correct order, as well as detect packet drops when they occur, typically, there are packet-level and instrument-level sequence numbers that the participants should check.\n",
    "Another thing that needs to be designed, both on the exchange market data publisher and the participants’ market data consumers, is a mechanism to recover from such packet drops. This same mechanism is also used by participants who join the market data stream after the markets are already open or if the participants need to restart their market data consumer components for any reason. In all such cases, the market data consumers in the client’s trading system needs to perform some synchronization to get the current and complete state of the limit order book.\n",
    "The commonly used design to achieve this synchronization is explained in this section. Usually, the exchange market data streams are broken down into two major groups – snapshot streams and incremental streams. We will explain the reason for having these two streams and how they help market participants handle cases of packet drops next.\n",
    "\n",
    "如前所述，交易所通常偏好使用 UDP 作为向参与者传送行情数据的网络协议。虽然这样可以加快数据传输并提高吞吐量，但也因为 UDP 的不可靠性而带来了丢包与乱序交付的风险。为了确保参与者按照正确顺序查看行情数据包并在发生丢包时及时发现，通常会在数据包级别和品种级别提供序列号，参与者需要进行检查。\n",
    "\n",
    "此外，交易所的行情数据发布端与参与者的行情数据消费者都需要设计机制来应对丢包。这套机制同样适用于以下情况：参与者在市场已开市后才加入行情流，或因某些原因需要重启行情数据消费者。在所有这些场景下，客户端交易系统中的行情数据消费者必须执行一定的同步操作，以获得当前且完整的限价订单簿状态。\n",
    "\n",
    "本节介绍了用于实现此类同步的常见设计。通常，交易所的行情数据流被划分为两大类——快照流与增量流。接下来我们将解释为何需要这两种流，以及它们如何帮助市场参与者处理丢包情况。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 增量行情数据流 / Incremental market data streams\n",
    "\n",
    "The incremental market data stream assumes that the market participant already has the correct view of the limit order book maintained by the matching engine, and this stream only publishes incremental updates to the previous state of the order book. This means that this stream is much lower in bandwidth requirements since it publishes only incremental updates to the book. Typically, under normal operating conditions, it is expected that market participants only be subscribed to the incremental streams to maintain the correct state of the order book.\n",
    "If the client drops a packet from this stream, then the state of the order book that they maintain might be inconsistent with what the matching engine has. The mechanism to handle this failure is to clear or reset the order book that the participant maintains. Then it needs to subscribe to the snapshot stream, which contains data for the full state of the entire order book (instead of only incremental updates) to synchronize to the correct state of the book once again. The protocol here is to clear the book, start queuing up incremental updates received from the incremental stream and wait to build the full state of the order book, and then apply the incremental updates to that full order book to finish the synchronization. Now, let us understand a few more details of what is published by the exchange on the snapshot market data streams.\n",
    "\n",
    "增量行情数据流假设市场参与者已经拥有与撮合引擎一致的限价订单簿视图，因此该流仅发布相对于订单簿上一状态的增量更新。也就是说，该流的带宽需求更低，因为它只发送订单簿的增量更新。在正常运行情况下，市场参与者通常只需订阅增量流即可维护正确的订单簿状态。\n",
    "\n",
    "如果客户端在该流中丢失了某个数据包，那么其维护的订单簿状态可能会与撮合引擎的不一致。处理此故障的机制是清空或重置参与者维护的订单簿，然后订阅快照流。快照流包含整个订单簿的完整状态（而非仅有增量更新），用以重新同步到正确的订单簿状况。具体流程是：清空订单簿，开始排队积累从增量流收到的增量更新消息，等待构建出完整的订单簿状态后，再将那些排队的增量更新应用到完整订单簿上完成同步。接下来，我们了解一下交易所在快照行情数据流上发布的内容细节。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 快照行情数据流 / Snapshot market data streams\n",
    "\n",
    "As we mentioned before, the snapshot market data stream contains data that can be used to build the full order book from a completely empty state. Usually, this stream just contains a thorough list of Order Add messages corresponding to every single passive order that exists in the order book. The exchange usually throttles how often this list is updated and published, meaning it might only send out a stream of snapshot messages every couple of seconds or so. This is because, since this stream contains information about all the orders in the order book for every single trading instrument, it can become quite bandwidth-heavy. Additionally, since packet drops are an exceedingly rare occurrence and participants do not mind waiting a few seconds when they first start up to grab the correct state of the order book, the throttling does not usually have a large negative impact.\n",
    "This concludes the discussion of the market data protocols and the synchronization process, so now we can design the market data consumer that we will implement.\n",
    "\n",
    "如前所述，快照行情数据流提供数据，使我们能够从完全空的状态构建完整的订单簿。通常，该流只包含订单簿中每一笔挂单对应的“订单新增”消息的完整列表。交易所通常会限制这一列表的更新与发布频率，例如可能每隔几秒才发送一组快照消息。这是因为该流包含了每个交易品种中所有订单簿挂单的信息，带宽需求较高。此外，由于丢包事件极其罕见，且参与者在初次启动时愿意等待几秒以获取正确的订单簿状态，因此这种限流通常不会带来明显的负面影响。\n",
    "\n",
    "至此，我们完成了对行情数据协议及同步流程的讨论，接下来就可以设计我们将要实现的行情数据消费者。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 设计行情数据消费者 / Designing the market data consumer\n",
    "\n",
    "Let us discuss a few design details for the market data consumer we will implement in our market participant’s trading system. We present only the market data consumer from Figure 5.1 here so we can discuss the design in greater detail.\n",
    "\n",
    "<img src=\"./images/5.6.jpg\" alt=\"图 5.6 – Design of our market data consumer infrastructure\" style=\"zoom:100%;\" />\n",
    "\n",
    "Let us discuss the two primary subcomponents when it comes to the design of the market data consumer infrastructure in a market participant’s trading system. Both use the socket utilities we built in the previous chapter in the C++ network programming using sockets section to subscribe to and consume the market data from the network.\n",
    "\n",
    "接下来我们讨论将在市场参与者交易系统中实现的行情数据消费者的若干设计细节。此处仅展示图 5.1 中的行情数据消费者，以便更详细地讨论其设计。\n",
    "\n",
    "- [中文版](./images/mermaid.md#5.6)\n",
    "\n",
    "在市场参与者交易系统的行情数据消费者基础设施中，有两个主要子组件。它们都使用我们在上一章“使用套接字进行 C++ 网络编程”部分构建的套接字工具，从网络中订阅并消费行情数据。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 快照与增量流同步器 / Snapshot and incremental stream synchronizer\n",
    "\n",
    "The market data consumer needs to have a subcomponent that can be used to subscribe to the snapshot stream in addition to being subscribed to the incremental stream. Remember that we explained that when a market participant’s system first starts up or needs to restart in the middle of the day or drops a market data packet from the incremental stream, it does not have the correct view of the limit order book. In such a scenario, the correct recovery/synchronization procedure is to clear the limit order book, subscribe to the snapshot stream, and wait till a full snapshot of the order book is received. Additionally, the updates that continue to come in over the incremental market data stream need to be queued up. Once the full snapshot is received and all incremental updates starting from the sequence number of the last update in the snapshot are also queued up and available, we are done. At this point, the limit order book is reconstructed from the snapshot stream, and all queued-up incremental updates are applied to this book to synchronize/catch up with the exchange. At this point, the consumer can stop consuming data from the snapshot stream and leave the snapshot stream, and only consume data from the incremental stream. The component in the market data consumer infrastructure responsible for this synchronization mechanism is what we will refer to as the snapshot and incremental stream synchronization subcomponent.\n",
    "\n",
    "行情数据消费者需要一个子组件，能够在订阅增量流的同时订阅快照流。回想我们之前的说明：当市场参与者的系统首次启动、在交易中途需要重启，或者在增量流中丢失了行情数据包时，它就不再拥有正确的限价订单簿视图。在这种情况下，正确的恢复/同步过程是：清空限价订单簿，订阅快照流，并等待接收到订单簿的完整快照。同时，增量行情数据流持续到来的更新必须排队缓存。一旦完整快照到达，并且自快照中最后一条更新的序列号开始的所有增量更新也已排队并可用，工作就完成了。此时，限价订单簿根据快照流重建，然后将所有排队的增量更新应用到该订单簿上，以与交易所同步/追赶。此时，消费者即可停止消费快照流的数据、离开快照流，只保留对增量流的消费。负责这一同步机制的组件，就是我们所称的“快照与增量流同步”子组件。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 行情数据协议解码器 / Market data protocol decoder\n",
    "\n",
    "The other subcomponent is responsible for decoding the stream of data coming in from the snapshot and/or the incremental market data streams. This component translates the data from the exchange feed format to the internal format of the trading strategy framework. This is usually a subset of the fields that the exchange provides and is often normalized across different trading exchanges to make the trading strategy framework independent of exchange-specific details. For our market data consumer infrastructure, we will keep this component quite simple since we will be using a packed binary structure, but as mentioned before, in practice, this can be a much more complicated format such as FAST.\n",
    "We discussed the details and design of how a market participant’s system consumes the public market data feed from the exchange. We can move on to the order gateway client infrastructure, which the participant uses to send order requests and receive responses and execution notifications.\n",
    "\n",
    "另一个子组件负责解码来自快照流和/或增量行情数据流的数据流。该组件将数据从交易所馈送格式转换为交易策略框架的内部格式。这通常是交易所提供字段的一个子集，并且常常跨不同交易所进行规范化，以使交易策略框架不依赖交易所的具体细节。在我们的行情数据消费者基础设施中，由于采用的是打包二进制结构，该组件将保持相当简单；但如前所述，实际中它可能需要处理更复杂的格式，例如 FAST。\n",
    "\n",
    "我们已经讨论了市场参与者系统如何消费交易所的公共行情数据馈送的细节与设计。接下来可以转向订单网关客户端基础设施，市场参与者用它来发送订单请求并接收响应与成交通知。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 理解订单网关客户端基础设施 / Understanding the order gateway client infrastructure\n",
    "\n",
    "The order gateway client infrastructure in the market participant’s trading system is a TCP client that connects to the exchange’s order gateway server. The other task that this component performs is receiving updates from the exchange on this TCP connection, decoding the messages received from the exchange order messaging protocol into a normalized internal format for use by the rest of the system. Finally, the order gateway client component is also responsible for taking the order actions requested by the trading framework and encoding them in the order messaging format that the exchange understands and sending it through to the exchange.\n",
    "\n",
    "The important thing to remember here is that the order gateway client must always maintain a reliable TCP connection to the exchange. This is to make sure that the exchange does not miss any order request from the client and that the client does not miss any updates to its orders from the exchange. In addition to the reliability mechanisms implemented by the TCP networking protocol itself, there usually exists an application-level reliability mechanism implemented by the exchange and the participants. This application-level reliability mechanism usually consists of strictly incrementing sequence numbers on the messages sent from exchange to client and from client to exchange. Additionally, there can be heart-beating mechanisms in place, which are simply messages sent from exchange to client and client to exchange to check whether a connection is still active during periods of low activity.\n",
    "\n",
    "Additionally, there are mechanisms to authenticate and identify clients when they first connect, and this is usually implemented as a handshake mechanism with user identification and password, and so on. There can be additional administrative messages, such as the logon authentication message, and it depends on the exchange and can have a wide range of purposes. For this book’s purposes, we will limit the scope to not focus on these administrative messages since they do not matter to our low latency goals.\n",
    "\n",
    "Next, let us design our order gateway client infrastructure.\n",
    "\n",
    "市场参与者交易系统中的订单网关客户端基础设施是一个 TCP 客户端，用于连接交易所的订单网关服务器。该组件还承担另一项任务：通过这条 TCP 连接接收来自交易所的更新，将来自交易所订单消息协议的消息解码成规范化的内部格式，供系统其他部分使用。最后，订单网关客户端组件还负责将交易框架请求的订单操作编码为交易所理解的订单消息格式，并发送给交易所。\n",
    "\n",
    "需要记住的重要一点是，订单网关客户端必须始终与交易所保持可靠的 TCP 连接。这是为了确保交易所不会错过客户端的任何订单请求，客户端也不会错过交易所对其订单的任何更新。除了 TCP 网络协议本身实现的可靠性机制之外，交易所和参与者通常还会实现应用级的可靠性机制。这种应用级可靠性机制通常包括：从交易所到客户端、以及从客户端到交易所发送的消息上，分别使用严格递增的序列号。此外，还可能存在心跳机制，即在低活动期间交易所和客户端互相发送消息，以检查连接是否仍然活跃。\n",
    "\n",
    "另外，在客户端首次连接时，还会有用于认证和识别客户的机制，通常以握手方式实现，并包含用户身份、密码等信息。还可能存在额外的管理类消息，例如登录认证消息等，这取决于交易所，目的也可能多种多样。就本书而言，我们会限缩范围，不会聚焦这些管理消息，因为它们与我们的低延迟目标无关。\n",
    "\n",
    "接下来，让我们设计订单网关客户端基础设施。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 设计订单网关客户端基础设施 / Designing the order gateway client infrastructure\n",
    "\n",
    "Let us discuss a few design details for the market data publisher we will implement in our market participant’s trading system. We present only the order gateway client from Figure 5.1 here so we can discuss the design in greater detail.\n",
    "\n",
    "Figure 5.7 – Design of our order gateway client infrastructure\n",
    "\n",
    "<img src=\"./images/5.7.jpg\" alt=\"图 5.7 – Design of our order gateway client infrastructure\" style=\"zoom:100%;\" />\n",
    "\n",
    "There are two simple components that make up the order gateway client inside the market participant’s trading system.\n",
    "\n",
    "让我们讨论将在市场参与者交易系统中实现的订单网关客户端的一些设计细节。此处仅展示图 5.1 中的订单网关客户端，以便更详细地讨论其设计。\n",
    "\n",
    "- [图 5.7 – 我们的订单网关客户端基础设施设计](./images/mermaid.md#5.7)\n",
    "\n",
    "在市场参与者的交易系统中，订单网关客户端由两个简单的组件组成"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### TCP 连接管理器 / TCP connection manager\n",
    "\n",
    "The order gateway client in a market participant’s trading system oversees connecting to the exchange order gateway server and managing that connection. In practice, a single participant will have multiple connections to the exchange for load balancing, redundancy, and latency reasons. But in the electronic trading ecosystem that we will be building, we will design it such that an order gateway client creates a single connection to the exchange order gateway server. We will use the TCP socket client library we built in the previous chapter under the C++ network programming using sockets section.\n",
    "\n",
    "市场参与者交易系统中的订单网关客户端负责连接到交易所订单网关服务器并维护该连接。实际中，单个参与者通常会出于负载均衡、冗余与延迟等原因，与交易所建立多条连接。但在我们将要构建的电子交易生态系统中，我们会设计为订单网关客户端仅创建一条到交易所订单网关服务器的连接。我们将使用上一章“使用套接字进行 C++ 网络编程”部分构建的 TCP 套接字客户端库。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 订单网关协议编解码器 / Order gateway protocol encoder and decoder\n",
    "\n",
    "The order messaging format encoder and decoder translate order requests from the internal format that the trading strategy uses to the exchange format and translate order responses and execution notifications from the exchange to an internal format for the strategy framework. This component can vary in complexity depending on the exchange format, but for our trading system, we will keep the encoding and decoding complexity low by using a binary-packed structure.\n",
    "Next, we move away from our discussion of the order gateway infrastructure and on to the most complex (and most interesting) component of the participant’s system – the trading strategy framework.\n",
    "\n",
    "订单消息格式的编码器和解码器负责将交易策略使用的内部格式的订单请求转换为交易所格式，同时把来自交易所的订单响应和成交通知转换为策略框架使用的内部格式。该组件的复杂度取决于交易所的格式，但在我们的交易系统中，将通过使用打包的二进制结构来保持较低的编码与解码复杂度。\n",
    "\n",
    "接下来，我们将从订单网关基础设施的讨论转向参与者系统中最复杂（也最有趣）的组件——交易策略框架。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 设计低延迟 C++ 交易算法框架 / Designing a framework for low-latency C++ trading algorithms\n",
    "\n",
    "Now that we have discussed the market data consumer and order gateway client components on the market participants’ trading systems, the last component we need to discuss is the framework that makes trading decisions. This component is one of the most important components in a trading system since this is where intelligence lies. By intelligence, we mean the system that processes normalized market data updates, builds a view into the market conditions, and computes trading analytics to find trading opportunities and execute trades. Obviously, this component relies on the market data consumer to receive decoded and normalized market data updates and uses the order gateway client component to send order requests to and receive order responses from the exchange in a decoded and normalized format.\n",
    "\n",
    "我们已经讨论了市场参与者交易系统中的行情数据消费者与订单网关客户端组件，最后需要讨论的是负责做出交易决策的框架。该组件是交易系统中最重要的部分之一，因为这里蕴藏着“智能”。所谓智能，是指系统处理标准化后的行情数据更新，构建对市场状况的认知，并计算交易分析以发现交易机会并执行交易。显而易见，该组件依赖行情数据消费者来接收解码并标准化的行情数据更新，同时使用订单网关客户端组件以解码和标准化的格式向交易所发送订单请求、接收订单响应。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 构建订单簿 / Building the order book\n",
    "\n",
    "The market participant needs to construct the limit order book based on the market data that the exchange publishes. Note that it is not strictly necessary that the client must build the entire order book, especially if the trading strategies do not require information that is that granular. For this book’s purposes, we will build a full order book in our trading framework, but we just wanted to point out that it is not strictly necessary in all cases. A simple example of such a case would be strategies that only care about knowing the prices and/or quantity of the most aggressively priced orders – that is, the highest bid price and lowest offer price (known as Top Of Book (TOB) or Best Bid and Offer(BBO)). Another example would be strategies that only rely on trade prices to make decisions and do not require a view into the full order book.\n",
    "\n",
    "One thing to reiterate here is that the order book that the client builds is slightly different from the one that the exchange maintains because the client does not usually know which order belongs to which market participant. Additionally, depending on the exchange, a few more pieces of information might be hidden from the market participants, such as which orders are icebergs, which new orders are stop orders, self-match-prevention considerations, and so on. Iceberg orders are orders that have a larger hidden quantity than what is displayed in the public market data. Stop orders are orders that lie dormant and then become active when a specific price trades. Self-match prevention (SMP) is a constraint that prevents a client from trading against themselves, which some exchanges choose to enforce in the matching engine. For the purposes of this book, we will ignore and not implement such special functionality. Another thing to understand is that the book that the trading participants have is a slightly delayed version of the order book that the matching engine has. This is because there is some latency between the matching engine updating its order book to the trading client getting the market updates corresponding to the changes and updating their book.\n",
    "\n",
    "市场参与者需要基于交易所发布的行情数据构建限价订单簿。需要注意的是，客户端并非必须构建整个订单簿，尤其是当交易策略不需要如此细粒度的信息时。就本书而言，我们将在交易框架中构建完整订单簿，但需要指出的是，在所有情况下这并非绝对必要。一个简单的例子是只关注最积极报价的策略——也就是最高买价和最低卖价（Top Of Book，TOB，也称为最佳买卖价，BBO）。另一个例子是仅依赖成交价做出决策、不需要完整订单簿视角的策略。\n",
    "\n",
    "需要再次强调的是，客户端构建的订单簿与交易所维护的订单簿略有不同，因为客户端通常不知道每笔订单属于哪个市场参与者。此外，根据不同交易所，部分信息可能对市场参与者隐藏，例如哪些订单是冰山单、哪些新订单是止损单、自成交防护（SMP）的考虑等等。冰山单的隐藏数量大于在公共行情数据中显示的数量；止损单是在特定价格成交时才激活的休眠订单；自成交防护是一种阻止客户端与自身成交的约束，一些交易所选择在撮合引擎中实施。出于本书目的，我们将忽略这些特殊功能。还需要理解的是，交易参与者所拥有的订单簿是撮合引擎订单簿的略微延迟版本，因为从撮合引擎更新订单簿到交易客户端获取相应的行情更新并更新自身订单簿之间存在一定延迟。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 构建特征引擎 / Building a feature engine\n",
    "\n",
    "Sophisticated trading strategies need to build additional intelligence over just the order book. These trading strategies need to implement various trading signals and intelligence on top of the prices, liquidity, trade transactions, and order book published by the exchange. The idea here is to build intelligence, which can be a combination of technical analysis style indicators, statistical predictive signals and models, and market microstructure-related statistical edges. A detailed discussion of various kinds of trading signals and predictive analysis is outside the scope of this book, but there are plenty of texts dedicated to this topic. There are many different terms used in practice for such predictive edges – trading signals, indicators, features, and so on. The component inside the trading system that constructs and wires together a collection of such predictive signals is often referred to as a feature/signal/indicator engine. In this book, we will build a minimal feature engine for our trading strategies, but we reiterate here that feature engines can get quite sophisticated and complicated depending on the strategy’s complexity.\n",
    "\n",
    "复杂的交易策略需要在订单簿之外构建额外的智能。这些策略需要在交易所发布的价格、流动性、成交记录和订单簿之上实现各种交易信号和智能。核心思想是构建智能，它可能是技术分析类指标、统计预测信号与模型、以及与市场微观结构相关的统计优势的组合。对各种交易信号与预测分析的详细讨论超出了本书范围，但市面上有大量专门讨论这一主题的书籍。在实践中，这类预测优势有许多称呼——交易信号、指标、特征等等。交易系统中负责构建并组装这类预测信号集合的组件通常被称为特征/信号/指标引擎。在本书中，我们将为交易策略构建一个最小化的特征引擎；但需要再次强调的是，特征引擎的复杂度可根据策略复杂程度变得非常高。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 开发执行逻辑 / Developing the execution logic\n",
    "\n",
    "After building the order book and deriving some trading signals from the current state of the market, if the trading strategies find an opportunity, they still need to execute their orders at the exchange. This is achieved by sending new orders, modifying existing orders to either move them to a more aggressive or less aggressive price, and/or canceling existing orders to avoid getting filled on them. The subcomponent in the trading infrastructure responsible for sending, modifying, and canceling orders – basically, managing a strategy’s orders at the exchange – is called the execution system. For the execution systems, the ability to react quickly to market data and order responses coming from the exchange and send out order requests as quickly as possible is extremely important. A large part of a high-frequency trading system’s profitability and sustainability depends on achieving the lowest possible latencies in the execution systems.\n",
    "\n",
    "在构建订单簿并从当前市场状态推导出交易信号之后，如果交易策略发现机会，仍需要在交易所执行订单。实现方式包括发送新订单、修改已有订单将其移动到更积极或更保守的价格，以及/或取消已有订单以避免被动成交。负责发送、修改、取消订单这一系列操作——也就是在交易所管理策略订单的基础设施子组件——称为执行系统。对于执行系统而言，能否快速响应来自交易所的行情数据和订单反馈，并尽快发出订单请求至关重要。高频交易系统的大部分盈利能力与可持续性都取决于执行系统实现尽可能低的延迟。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 理解风险管理系统 / Understanding the risk management systems\n",
    "\n",
    "The risk management system is an important piece of the trading strategy infrastructure. Technically speaking, in practice, there are multiple layers of risk management systems in a modern electronic trading ecosystem. In practice, there are risk management systems in the client’s trading strategy framework, order gateway clients in the market participant’s system, and backend systems at the clearing broker’s end. For this book’s purposes, we will only implement a minimal risk management system in the trading strategy framework. Risk management systems try to manage the different forms of risk, as depicted in the following diagram: \n",
    "\n",
    "Figure 5.8 – Different risk metrics in an automated risk management system\n",
    "\n",
    "<img src=\"./images/5.8.jpg\" alt=\"图 5.8 – Different risk metrics in an automated risk management system\" style=\"zoom:100%;\" />\n",
    "\n",
    "Let us discuss these risk measures in more detail next.\n",
    "\n",
    "风险管理系统是交易策略基础设施中的重要组成部分。严格来说，在现代电子交易生态系统中，现实中存在多层风险管理系统：客户端交易策略框架中的风险管理、市场参与者系统内订单网关客户端的风险管理，以及清算经纪后台系统的风险管理。本书仅在交易策略框架内实现一个最小化的风险管理系统。风险管理系统尝试应对不同形式的风险，如下图所示：\n",
    "\n",
    "- [图 5.8——自动化风险管理系统中的不同风险指标](./images/mermaid.md#5.8)\n",
    "\n",
    "接下来我们将更详细地讨论这些风险度量。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 基于订单数量的风险 / Risk based on order quantities\n",
    "\n",
    "One measure that a lot of trading systems care about is the maximum possible quantity that the algorithm is allowed to send for a single order. This is mostly to prevent bugs and user errors in the system where the algorithm ends up accidentally sending an order much larger than expected. These types of errors in practice are referred to as Fat Finger errors, to refer to what would happen if a user accidentally pressed more keys than intended.\n",
    "\n",
    "许多交易系统关注的一个指标，是算法在单笔订单中被允许发送的最大数量。这样做主要是为了防止系统出现程序漏洞或用户失误，导致算法意外地下出远超预期规模的订单。这类错误在实践中通常被称为“胖手指”（Fat Finger）错误，形容用户无意间多按了键导致的问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 基于公司持仓的风险 / Risk based on firm positions\n",
    "\n",
    "An obvious measure of risk is the position that a strategy has in a certain trading instrument. The size of the position directly dictates how much money is lost if the market prices change a certain amount. This is why the realized position that a strategy or a firm has in a certain trading instrument is an extremely important one and is closely monitored to make sure it falls within agreed-upon limits. Note that realized position is the position that the strategy currently holds, and this ignores additional orders that the strategy might have, which might increase or decrease the position on execution.\n",
    "\n",
    "一个显而易见的风险衡量标准，是某个策略在特定交易品种上的持仓。持仓规模会直接决定当市场价格发生一定幅度变动时会亏损多少资金。因此，策略或公司在某个交易品种上的实际持仓是一项极其重要的指标，需要被密切监控，以确保其保持在约定的限额之内。请注意，这里所说的实际持仓，是指策略目前真正持有的仓位，不包含策略仍在市场中挂着、可能在成交后增加或减少持仓的订单。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 基于最坏情形持仓的风险 / Risk based on worst-case position\n",
    "\n",
    "Note that in the last section, we mentioned that realized position metrics ignore how many additional live orders exist in the market. The worst-case scenario position metric tracks what the position would be considering the live orders on the side that would increase the realized position along with the actual realized position. This means if the strategy or firm is long (position from buying an instrument), then it will also check how much additional unexecuted buy quantity the strategy has in the market to compute the absolute worst-case position. This is important because some strategies might never build up to large positions but might always have a lot of active orders in the market. The perfect example of such a strategy is a market-making strategy, which we will see later in this book, but the point here is that it is important to think about worst-case scenarios when it comes to risk management.\n",
    "\n",
    "上一节我们提到，实际持仓指标忽略了市场中尚未成交的挂单数量。最坏情形持仓指标会同时考虑当前实际持仓，以及在成交后会增加该持仓方向的所有挂单，以跟踪“最坏情况下”可能出现的仓位。这意味着，如果策略或公司当前是多头（买入导致的仓位），那么它还要检查市场中尚未成交的额外买单数量，以计算绝对的最坏情形持仓。这一点很重要，因为有些策略可能永远不会积累很大的实际持仓，但始终在市场中挂着大量订单。典型例子就是市场做市策略，本书后面会介绍。关键在于，风险管理时必须考虑最坏情形。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 管理已实现与未实现亏损的风险 / Risk to manage realized and unrealized loss\n",
    "\n",
    "This is what most people think of when they think of risk in the context of electronic trading. This risk metric tracks and imposes limits on how much money a strategy or firm has lost. If that value crosses a certain threshold, then depending on how much the firm has in its brokerage account, how much collateral they have, and so on, there can be consequences for the firm. Not only is it important to track the realized losses for a strategy when the strategy opens and closes positions but it is also important to track the open positions against the market prices.\n",
    "To understand this, let us explain the following scenario: a strategy buys a certain quantity of an instrument and then sells the same amount of quantity back at a lower price where the strategy has a realized loss and no open position. Now, let us say the strategy buys some amount of the trading instrument, and then after the purchase where the strategy has a long position, the price of the instrument in the market moves lower. Here, this strategy not only has the realized loss it is carrying from the previous set of trades but now it also has an unrealized loss on this most recently opened long position. The risk management system needs to compute realized and unrealized losses in near real time to get an accurate view of the actual risk.\n",
    "\n",
    "谈到电子交易中的风险，大多数人首先想到的就是这一类指标。它用于跟踪并设定策略或公司已经亏损的金额上限。如果亏损金额超过某个阈值，那么根据公司在经纪账户中的资金、所持抵押品等情况，可能会产生相应后果。不仅需要在策略开仓和平仓时跟踪已实现亏损，还必须同时监控未平仓头寸相对于市场价格的变化。\n",
    "\n",
    "举例来说：策略买入某个品种的一定数量，然后以更低的价格卖出同样数量，此时策略产生了已实现亏损，且没有未平仓头寸。再比如，策略买入该品种后持有多头仓位，如果市场价格随后下跌，那么策略不仅背负着之前交易的已实现亏损，还要面对这笔新多头仓位的未实现亏损。风险管理系统需要近乎实时地计算已实现与未实现亏损，才能准确掌握真实风险。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 基于成交量的风险 / Risk based on traded volumes\n",
    "\n",
    "This measure is not necessarily a risk; a strategy that trades a lot of volume on a certain day or in general is not a problem in itself. This risk metric seeks mostly to prevent runaway algorithms from overtrading in the market in cases of software or configuration bugs or just unexpected market conditions. This can be implemented in many ways, but the simplest implementation would be to have a cap on how much volume a strategy is allowed to trade for a trading instrument before it automatically stops sending any new orders or trading further. Usually, at this point, an external human operator needs to make sure that the algorithm behavior is as expected and then resume the trading strategy or stop it.\n",
    "\n",
    "这一指标不一定是传统意义上的风险：策略在某天或平时成交量很大并不必然是问题。本指标主要用于防止算法因为软件或配置错误，或是出乎意料的市场状况，而在市场中过度交易。实现方式多种多样，但最简单的做法是为策略在某个品种上设定一个最大可累计成交量的上限，一旦达到就自动停止发送新订单或继续交易。通常在这一刻，需要外部人工来确认算法行为是否符合预期，再决定恢复或终止该策略。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 管理订单、成交与亏损速率的风险 / Risk to manage rate of orders, trades, and losses\n",
    "\n",
    "The risk metrics we will discuss in this subsection fall into the rate-based risk management category. What we mean by rate-based is that the risk is computed for sliding windows of time to make sure that the strategy does not send too many orders in each window, does not trade too much in each time window, does not lose too much money in each window, and so on. Again, these metrics are to prevent trading strategy behavior that is unexpected or resembles an out-of-control or runaway algorithm. These are implemented by either resetting the counters for the underlying metric (number of orders or number of trades or traded volume or loss) at the end of the time-period window or using a rolling counter of these metrics. These risk metrics also implicitly prevent the trading strategy from behaving unexpectedly during periods of super-heightened volatility or flash crash-style scenarios.\n",
    "Finally, we will design the last major component in our electronic trading ecosystem.\n",
    "\n",
    "本节讨论的风险指标属于基于速率的风险管理。所谓“基于速率”，是指在滑动时间窗口内计算风险，以确保策略在每个时间窗口内不会发送过多订单、产生过多成交，或遭受过多亏损。这些指标旨在防止策略出现意料之外的行为，或看起来像失控算法。实现方法包括在每个时间窗口结束时重置相关计数器（如订单数、成交数、成交量或亏损），或使用滚动计数。这类风险指标还可以在极端市场波动或“闪崩”这类情形下，隐性地限制策略的异常行为。\n",
    "\n",
    "最后，我们将设计电子交易生态系统中的最后一个主要组件。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 设计我们的交易策略框架 / Designing our trading strategy framework\n",
    "\n",
    "Let us discuss a few design details for the trading strategy framework we will implement in our participant’s trading system. We present only the trading strategy framework from Figure 5.1 here so we can discuss the design in greater detail. \n",
    "\n",
    "Figure 5.9 – Design of our trading strategy framework\n",
    "\n",
    "<img src=\"./images/5.9.jpg\" alt=\"图 5.9 – Design of our trading strategy framework\" style=\"zoom:100%;\" />\n",
    "\n",
    "Now we will discuss the design of the major subcomponents in the trading strategy framework we will build in this book. Note that we use the terms trading strategy framework and trading engineinterchangeably and they mean the same thing in the context of this book – a collection of components to house and run automated trading algorithms.\n",
    "\n",
    "接下来，我们来讨论将在参与者交易系统中实现的交易策略框架的若干设计细节。这里仅展示图 5.1 中的交易策略框架，以便更深入地讨论其设\n",
    "\n",
    "- [图 5.9——我们交易策略框架的设计](./images/mermaid.md#5.9)\n",
    "\n",
    "现在，我们将讨论本书将要构建的交易策略框架中各个主要子组件的设计。需要注意的是，在本书的语境中，“交易策略框架”（trading strategy framework）与“交易引擎”（trading engine）这两个术语可以互换使用，它们都指代同一个概念——用于承载并运行自动化交易算法的组件集合。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 限价订单簿 / Limit order book\n",
    "\n",
    "The limit order book in the trading strategy framework is similar to the one that the exchange matching engine builds. Obviously, the goal here is not to perform matching between orders but instead to build, maintain, and update the limit order book from the market data updates consumed by the market data consumer via the lock-free queue. The requirement to support efficient insertion, modification, and deletion of orders into this book still applies here. The other goal here is to also make this order book accessible for use cases that the feature engine and trading strategy components require. There can be various use cases; one example is being able to synthesize a BBO or TOB quickly and efficiently for components that only need the best prices and quantities. Another example would be the ability to track the strategy’s own orders in the limit order book to find where they are in the FIFO queue at a price level. Yet another example would be the ability to detect executions on a strategy’s orders from the public market data feed, which can be a big boost during times when the private order feed is lagging behind the public market data feed. Implementing these details in the trading strategies we build in this book is out of the scope of what we can cover. But in practice, these details are extremely important since the advantage one gains from detecting executions from both the order responses and market data can be tens, hundreds, or even thousands of microseconds in latency. Here, we will use the lock-free queue that we built in the Transferring data using lock-free queues section and the memory pool we built in the Designing C++ memory pools to avoid dynamic memory allocations section in the previous chapter, Building the C++ Building Blocks for Low-Latency Applications.\n",
    "\n",
    "交易策略框架中的限价订单簿与交易所撮合引擎所维护的订单簿类似。显然，我们的目标不是撮合买卖订单，而是根据市场数据消费者通过无锁队列获取的行情更新，构建、维护并刷新这一订单簿。高效地向订单簿插入、修改、删除订单的需求在此仍然适用。我们还希望让特征引擎和交易策略等组件能够随时访问订单簿，以满足不同的业务需求。例如，快速、准确地合成最佳买卖价（BBO）或盘口顶层（TOB），供只需要最佳价和数量的组件使用；再例如，追踪策略自身订单在订单簿中的位置，以判断它们在某个价位的 FIFO 队列中的排位；又或者，从公共行情中侦测策略订单的成交，这在私有订单回报落后于公共行情时尤为重要，能显著缩短几十、几百甚至上千微秒的延迟。尽管本书不会实现上述细节，但在实务中至关重要。本章节会复用上一章《为低延迟应用构建 C++ 基础模块》中“使用无锁队列传输数据”小节实现的无锁队列，以及“设计 C++ 内存池以避免动态内存分配”小节实现的内存池。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 特征引擎 / Feature engine\n",
    "\n",
    "We mentioned before that we will be building a minimal feature engine in this book. Our feature engine will only support a single feature computed from the data available from our order book, and this single feature will be used to drive our trading strategies. This feature will be updated when there are substantial changes to the order book in terms of price or liquidity and as trades occur in the market. When the feature is updated, the trading strategy can use the new feature value to re-evaluate its position, live orders, and so on to make a trading decision.\n",
    "\n",
    "我们之前提到，本书将构建一个最简化的特征引擎。它只会基于订单簿数据计算单一特征，而该特征将作为驱动交易策略的唯一信号。每当订单簿在价格或流动性方面出现显著变化，或市场发生成交时，这个特征就会更新。特征更新后，交易策略会利用新的特征值重新评估自身仓位、挂单等状态，并做出交易决策。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 交易策略 / Trading strategy\n",
    "\n",
    "The trading strategy is the component that finally makes the trading decisions based on a multitude of factors. The trading decisions depend on the trading algorithm itself, the feature values from the feature engine, the state of the order book, the prices and FIFO position of the strategy’s orders in the order book, risk evaluations from the risk manager, the state of the live orders from the order manager, and so on. This is where most of the complexity of the trading strategy framework resides because it needs to handle a lot of different conditions and execute the orders safely and profitably. In this book, we will build two distinct kinds of basic trading algorithms – market-making, also known as liquidity-providing strategies, and taking strategies, also known as liquidity-removing strategies. Market-making strategies have passive orders in the book and rely on other market participants crossing the spread to trade against us. Liquidity-taking strategies are the strategies that cross the spread and send aggressive orders to take out passive liquidity.\n",
    "\n",
    "交易策略组件会综合多方面因素做出最终交易决策。这些决策取决于交易算法本身、特征引擎提供的特征数值、订单簿状态、策略订单在订单簿中的价格与 FIFO 排位、风险管理器的评估、订单管理器中活动订单的状态等等。这里聚集了交易策略框架的主要复杂度，因为它必须处理大量情形，并确保安全、盈利地执行订单。本书将实现两类基础策略：做市（流动性提供）策略和吃单（流动性移除）策略。做市策略在订单簿中挂出被动订单，依赖其他参与者跨越价差来与我们成交；吃单策略则主动跨价差，发送进取型订单以拿掉被动流动性。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 订单管理器 / Order manager\n",
    "\n",
    "The order manager component is an abstraction that hides the lower-level details of sending order requests, managing the states of active orders, handling in-flight conditions (we will explain this shortly) for these orders, handling responses from the exchange, handling scenarios around partial and full executions of orders, and managing positions. The order manager also builds and maintains a couple of different data structures to track the state of the strategy’s orders. In some sense, the order manager is like the limit order book, except it manages a tiny subset of the orders that belong to the strategy.\n",
    "\n",
    "On the other hand, there is some additional complexity in the order management since there are some cases where order requests are in-flight from the market participant to the exchange and some events happen in the exchange matching engine at the same time. An example of an in-flight condition would be the scenario where the client tries to cancel an active order and sends a cancel request to the exchange. But while this cancel request is in-flight to the exchange, the matching engine at the exchange executes the order because an aggressor that would match this order shows up. Then, by the time the cancel request finally reaches the matching engine, the order is already executed and removed from the limit order book at the exchange, resulting in a cancel reject for this request. The order manager needs to be able to handle all different scenarios like this one accurately and efficiently.\n",
    "In this book, we will build an order manager that can be used to manage passive and aggressive orders and can handle all these different conditions.\n",
    "\n",
    "订单管理器抽象了发送订单请求的底层细节，负责维护活动订单的状态、处理在途（in-flight）状态、处理交易所返回的各种响应、应对订单的部分或全部成交，以及管理仓位。它还会构建并维护多种数据结构来追踪策略订单的状态。从某种意义上讲，订单管理器与限价订单簿相似，但只管理属于策略的那一小部分订单。\n",
    "\n",
    "另一方面，订单管理也包含额外的复杂度，因为当订单请求从市场参与者发往交易所的过程中（即在途状态），交易所撮合引擎可能会同时发生事件。一个典型的在途场景是：客户端尝试撤销某个活动订单，并向交易所发送撤单请求；然而，在撤单请求抵达之前，交易所撮合引擎因出现对手方而撮合了该订单。等撤单请求抵达时，该订单已被执行并从交易所的订单簿中移除，最终得到撤单被拒的响应。订单管理器必须准确、高效地处理类似场景。本书将构建一个能够管理被动与进取订单并处理各种在途情况的订单管理器。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 风险管理器 / Risk manager\n",
    "\n",
    "The risk manager tracks the different risk metrics we described in the previous section, Understanding the risk management systems. Additionally, the risk manager needs to notify the trading strategy about events where risk limits are breached so that the trading strategy can reduce risk and/or shut down safely. In our trading infrastructure, we will implement a few basic risk metrics, such as position, total loss, and messaging rates for order requests.\n",
    "\n",
    "风险管理器用于跟踪上一节《理解风险管理系统》中提到的各类风险指标。此外，当风险限额被触发时，它需要通知交易策略，使策略能够降低风险或安全停止。在我们的交易基础设施中，将实现若干基础风险指标，包括仓位、累计亏损以及订单请求的报文速率等。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 总结 / Summary\n",
    "\n",
    "This concludes our discussion of the details and design of the major components in our electronic trading ecosystem. Let us summarize the concepts, components, and interactions we discussed, as well as the design of the components that build the electronic trading ecosystem we will build.\n",
    "We started off by presenting the topology of the electronic trading ecosystem. This consists of the electronic trading exchange and many market participants that want to trade on that exchange. The electronic trading exchange infrastructure itself consists of three major components at a high level – the matching engine, the market data publisher, and the order gateway server infrastructure. From a market participant’s perspective, the major components are the market data subscriber and consumer, the trading strategy framework with all its subcomponents, and the order gateway client infrastructure.\n",
    "\n",
    "Next, we performed a deep dive into the exchange matching engine details. We explained the responsibilities of this component and how it builds, maintains, and updates the limit order book and matches participant orders that cross against each other. We concluded that section by designing our simplified matching engine component and its subcomponents, which we will implement in the next chapter.\n",
    "\n",
    "The next topics of discussion were the market data publisher and the order gateway server infrastructure at the exchange. We described in great detail the different messages that the market data feed is composed of, the market data feed protocol, as well as designing the components inside the market data publisher. We also discussed the order gateway server, which the exchange hosts as an endpoint for the market participants to connect to, forward order requests, and receive order responses and notifications for their orders getting executed by the matching engine. We presented the design of the order gateway server with all its subcomponents, which we will implement in later chapters of this book.\n",
    "\n",
    "The section following that took a look at the market participants’ trading systems. First, we discussed the details for the market data consumer and the order gateway client infrastructure, which the participants use to consume the public market data feed from the exchange and connect to and communicate with the exchange. We also presented and discussed the design of the market data consumer we will build, as well as how it synchronizes and decodes the exchange market data feed. Finally, we designed the order gateway client infrastructure, which the trading system will use to connect to and communicate with the exchange’s order gateway server infrastructure.\n",
    "\n",
    "The final section of this chapter was dedicated to describing and designing the framework for trading strategies. We described the major components we will need to build this framework – the order book, the feature engine, the execution logic framework, and the risk management subcomponent. Finally, we laid out the design of the trading infrastructure we will build so that you can understand the higher-level design of this component before we dive into the lower-level details in subsequent chapters.\n",
    "\n",
    "The next chapter jumps into the implementation details of the matching engine framework we designed in this chapter. Note that we will reuse a lot of the basic building blocks we built in the previous chapter moving forward as we implement our electronic trading ecosystem. The motivation for building the basic building blocks will become clearer as we implement the rest of the system, starting in the next chapter.\n",
    "\n",
    "至此，我们已经完成了对电子交易生态系统主要组件的细节与设计的讨论。下面对我们涵盖的概念、组件、交互方式，以及构成整个电子交易生态系统的设计进行回顾。\n",
    "\n",
    "我们首先介绍了电子交易生态系统的拓扑结构，其中包括电子交易所，以及希望在该交易所交易的众多市场参与者。电子交易所的基础设施在高层面由三个主要组件构成：撮合引擎、行情发布器以及订单网关服务器基础设施。对于市场参与者而言，关键组件包括行情订阅与消费模块、包含诸多子组件的交易策略框架，以及订单网关客户端基础设施。\n",
    "\n",
    "接着，我们深入解析了交易所撮合引擎的细节，说明了该组件的职责，以及如何构建、维护、更新限价订单簿，并撮合对敲的参与者订单。随后，我们设计了简化版的撮合引擎组件及其子组件，这将于下一章实现。\n",
    "\n",
    "随后讨论的内容是交易所内部的行情发布器与订单网关服务器基础设施。我们详尽描述了行情数据源包含的不同消息、行情数据协议，并设计了行情发布器内部的组件。同时，我们也讨论了订单网关服务器——交易所提供给市场参与者连接、转发订单请求、并接收撮合引擎执行结果通知的终端。我们展示了订单网关服务器及其全部子组件的设计，这些将在本书后续章节实现。\n",
    "\n",
    "之后，我们审视了市场参与者的交易系统。首先讨论了行情消费者以及订单网关客户端基础设施，市场参与者通过它们接收交易所的公共行情数据，并与交易所进行通信。我们展示并讲解了准备构建的行情消费者及其同步、解码交易所行情数据的方式。最后，我们设计了交易系统用于连接并沟通交易所订单网关服务器的订单网关客户端基础设施。\n",
    "\n",
    "本章的最后一部分专注于描述和设计交易策略框架。我们介绍了构建该框架所需的主要组件——订单簿、特征引擎、执行逻辑框架以及风险管理子系统。最后，我们展示了即将构建的交易基础设施的整体设计，使读者在深入后续章节、了解底层细节之前，先把握这一组件的高层架构。\n",
    "\n",
    "下一章将深入实现本章设计的撮合引擎框架。需要注意的是，我们会复用上一章构建的诸多基础模块，随着我们逐步实现电子交易生态系统，其设计初衷也会愈发清晰。下一章起，我们将以此为起点完成系统的其余部分。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++17",
   "language": "C++17",
   "name": "xcpp17"
  },
  "language_info": {
   "name": "C++17"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
